package org.mybatis.generator.api.plus;

import java.util.*;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;

/** @author wangfupeng */
public class MybatisPluginService extends PluginAdapter {

    private FullyQualifiedJavaType slf4jLogger;
    private FullyQualifiedJavaType slf4jLoggerFactory;
    private FullyQualifiedJavaType autowired;
    private FullyQualifiedJavaType cacheEvict;
    private FullyQualifiedJavaType cacheable;
    private FullyQualifiedJavaType service;
    private FullyQualifiedJavaType string;
    private FullyQualifiedJavaType integer;
    private FullyQualifiedJavaType longT;
    private FullyQualifiedJavaType listT;

    private FullyQualifiedJavaType serviceType;
    private FullyQualifiedJavaType mapperType;
    private FullyQualifiedJavaType modelType;
    private FullyQualifiedJavaType modelExampleType;
    private FullyQualifiedJavaType javaUtilListType;
    private FullyQualifiedJavaType cacheForFetchLater;
    private FullyQualifiedJavaType cacheMockThenFetchLater;

    private String basePackage;
    private String servicePackage;
    private String project;
    private String modelUrl;
    private String aspectPackage;
    private String eurekaServiceName;
    private String feignClientUrl;
    private FullyQualifiedJavaType listResponse;
    private FullyQualifiedJavaType simpleResponse;
    private FullyQualifiedJavaType stringType;
    private FullyQualifiedJavaType integerType;
    private FullyQualifiedJavaType booleanType;
    private FullyQualifiedJavaType byteType;
    private FullyQualifiedJavaType doubleType;
    private FullyQualifiedJavaType floatType;
    private FullyQualifiedJavaType longType;
    private FullyQualifiedJavaType shortType;
    private FullyQualifiedJavaType bigDecimalType;
    private FullyQualifiedJavaType dateType;

    /**
     * 所有的方法
     */
    private List<Method> methods;

    /**
     * 是否添加注解
     */
    public MybatisPluginService() {
        super();
        // default is slf4j
        slf4jLogger = new FullyQualifiedJavaType("org.slf4j.Logger");
        slf4jLoggerFactory = new FullyQualifiedJavaType("org.slf4j.LoggerFactory");
        autowired = new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired");
        service = new FullyQualifiedJavaType("org.springframework.stereotype.Service");
        autowired = new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired");
        cacheEvict = new FullyQualifiedJavaType("org.springframework.cache.annotation.CacheEvict");
        cacheable = new FullyQualifiedJavaType("org.springframework.cache.annotation.Cacheable");
        listT = new FullyQualifiedJavaType("java.util.List");
        string = new FullyQualifiedJavaType("java.lang.String");
        integer = new FullyQualifiedJavaType("java.lang.Integer");
        longT = new FullyQualifiedJavaType("java.lang.Long");
        methods = new ArrayList<Method>();
    }

    /**
     * 读取配置文件
     */
    @Override
    public boolean validate(List<String> warnings) {
        this.modelUrl = context.getJavaModelGeneratorConfiguration().getTargetPackage();
        this.basePackage = properties.getProperty("basePackage");
        this.servicePackage = properties.getProperty("targetPackage");
        this.project = properties.getProperty("targetProject");
        this.aspectPackage = properties.getProperty("aspectPackage");
        this.eurekaServiceName = properties.getProperty("eurekaServiceName");
        this.feignClientUrl = properties.getProperty("feignClientUrl");
        cacheForFetchLater = new FullyQualifiedJavaType(aspectPackage + ".CacheForFetchLater");
        cacheMockThenFetchLater = new FullyQualifiedJavaType(aspectPackage + ".CacheMockThenFetchLater");
        return true;
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        List<GeneratedJavaFile> files = new ArrayList<GeneratedJavaFile>();
        String table = introspectedTable.getBaseRecordType();// 取Service名称【com.coolead.service.PetService】
        String tableName = table.replaceAll(this.modelUrl + ".", "");
        mapperType = new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType());
        modelType = new FullyQualifiedJavaType(modelUrl + "." + tableName);
        modelExampleType = new FullyQualifiedJavaType(modelUrl + "." + tableName + "Example");
        javaUtilListType = new FullyQualifiedJavaType("java.util.List");

        simpleResponse = new FullyQualifiedJavaType(basePackage+".controller.response.SimpleResponse");
        listResponse = new FullyQualifiedJavaType(basePackage+".controller.response.ListResponse");
        stringType = new FullyQualifiedJavaType("java.lang.String");
        integerType = new FullyQualifiedJavaType("java.lang.Integer");
        booleanType = new FullyQualifiedJavaType("java.lang.Boolean");
        byteType = new FullyQualifiedJavaType("java.lang.Byte");
        doubleType = new FullyQualifiedJavaType("java.lang.Double");
        floatType = new FullyQualifiedJavaType("java.lang.Float");
        longType = new FullyQualifiedJavaType("java.lang.Long");
        shortType = new FullyQualifiedJavaType("java.lang.Short");
        bigDecimalType = new FullyQualifiedJavaType("java.lang.BigDecimal");
        dateType = new FullyQualifiedJavaType("java.util.Date");
        serviceType = new FullyQualifiedJavaType(servicePackage + "." + tableName + "Service");
        TopLevelClass topLevelClass = new TopLevelClass(serviceType);
        // 导入必须的类
        addImport(topLevelClass);
        addLogger(topLevelClass);
        addService(topLevelClass, introspectedTable, tableName, files);
        return files;
    }

    /**
     * import must class 导入必要的class
     */
    private void addImport(TopLevelClass topLevelClass) {
        // addImportedType是往一个set里面加数据，所以可以重复添加。
        topLevelClass.addImportedType(mapperType);
        topLevelClass.addImportedType(modelType);
        topLevelClass.addImportedType(modelExampleType);
        topLevelClass.addImportedType(javaUtilListType);
        topLevelClass.addImportedType(slf4jLogger);
        topLevelClass.addImportedType(slf4jLoggerFactory);
        topLevelClass.addImportedType(service);
        topLevelClass.addImportedType(autowired);
        topLevelClass.addImportedType(cacheEvict);
        topLevelClass.addImportedType(cacheable);
        topLevelClass.addImportedType(string);
        topLevelClass.addImportedType(cacheForFetchLater);
        topLevelClass.addImportedType(cacheMockThenFetchLater);
    }

    /**
     * import logger
     */
    private void addLogger(TopLevelClass topLevelClass) {
        Field field = new Field();
        field.setFinal(true);
        field.setInitializationString("LoggerFactory.getLogger(" + topLevelClass.getType().getShortName() + ".class)"); // set
        // value
        field.setName("logger"); // set var name
        field.setStatic(true);
        field.setType(new FullyQualifiedJavaType("Logger")); // type
        field.setVisibility(JavaVisibility.PUBLIC);
        topLevelClass.addField(field);
    }

    /**
     * add implements class
     *
     * @param introspectedTable
     * @param tableName
     * @param files
     */
    protected void addService(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String tableName,
                              List<GeneratedJavaFile> files) {
        topLevelClass.setVisibility(JavaVisibility.PUBLIC);
        // set service
        topLevelClass.addAnnotation("@SuppressWarnings(\"rawtypes\")");
        topLevelClass.addAnnotation("@Service");
        topLevelClass.addImportedType(service);
        // add import mapper
        addField(topLevelClass, tableName);
        // add method
        topLevelClass.addMethod(addModelMethod(introspectedTable, tableName));
        String pathForPrimaryKeyColumns = MybatisUtilCommon.getPathForPrimaryKeyColumns(introspectedTable);
        if (!"".equals(pathForPrimaryKeyColumns)) {
            topLevelClass.addMethod(deleteByPrimaryKeyMethod(introspectedTable, tableName));
            if(MybatisUtilCommon.getExistNonePrimaryKeyColumns(introspectedTable)) {
                topLevelClass.addMethod(updateMethod(introspectedTable, tableName));
                topLevelClass.addMethod(selectByPrimaryKeyMethod(introspectedTable, tableName));
                topLevelClass.addMethod(selectByPrimaryKeyWithoutCacheMethod(introspectedTable,tableName));
                topLevelClass.addMethod(selectByPrimaryKeyWithCacheForFetchLaterMethod(introspectedTable,tableName));
                topLevelClass.addMethod(selectByPrimaryKeyCacheMockThenFetchLater(introspectedTable,tableName));
            }
        }
        topLevelClass.addMethod(updateByExampleMethod(introspectedTable, tableName));
        topLevelClass.addMethod(list(introspectedTable, tableName));
        topLevelClass.addMethod(listWithoutCache(introspectedTable, tableName));
        topLevelClass.addMethod(listCacheMockThenFetchLater(introspectedTable, tableName));
        topLevelClass.addMethod(countByExample(introspectedTable, tableName));
        topLevelClass.addMethod(countByExampleWithoutCache(introspectedTable, tableName));
        topLevelClass.addMethod(countByExampleCacheMockThenFetchLater(introspectedTable, tableName));
        GeneratedJavaFile file = new GeneratedJavaFile(topLevelClass, project, context.getJavaFormatter());
        files.add(file);
        MybatisUtilResponse.writeSimpleResponse(basePackage, project);
        MybatisUtilResponse.writeListResponse(basePackage, project);
        MybatisUtilDistinctMapper.writeDistinctMapper(basePackage, aspectPackage, project, topLevelClass, introspectedTable, tableName);
        MybatisUtilDistinctService.writeDistinctService(basePackage, aspectPackage, project, topLevelClass, introspectedTable, tableName, servicePackage);
        MybatisUtilFeignClient.writeFeignClient(eurekaServiceName, feignClientUrl, basePackage, aspectPackage, project, topLevelClass, introspectedTable, tableName, servicePackage);
        MybatisUtilFeignWrapper.writeFeignWrapper(basePackage, aspectPackage, project, topLevelClass, introspectedTable, tableName, servicePackage);
    }

    protected void addField(TopLevelClass topLevelClass, String tableName) {
        Field field = new Field();
        field.setName(getMapperShort());
        topLevelClass.addImportedType(mapperType);
        field.setType(mapperType);
        field.setVisibility(JavaVisibility.PRIVATE);
        field.addAnnotation("@Autowired");
        topLevelClass.addField(field);
    }

    protected Method addModelMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("add");
        method.setReturnType(integer);
        method.addParameter(new Parameter(modelType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(".setAllFieldsNotNull();");
        method.addBodyLine(sb.toString());
        sb.setLength(0);
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("insert");
        sb.append("(");
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(");");
        method.addBodyLine(sb.toString());
        return method;
    }

    private String getMapperShort() {
        return MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName());
    }

    private String getMapperShortWithDot() {
        return MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".";
    }

    protected Method selectByPrimaryKeyMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("getWithCache");
        method.setReturnType(modelType);
        List<IntrospectedColumn> introspectedColumns;
        MybatisUtilCommon.addParameterForPrimaryKeyColumnsInMethodSelectByPrimaryKey(method, introspectedTable);
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("selectByPrimaryKey");
        sb.append("(");
        sb.append(MybatisUtilCommon.getParameterValueForPrimaryKeyColumns(introspectedTable));
        sb.append(");");
        method.addBodyLine(sb.toString());
        method.addAnnotation("@Cacheable(value = \"cache:" + modelType.getShortName().toLowerCase()
                + "\", key = \"'cache:" + modelType.getShortName().toLowerCase() + ":get'"+MybatisUtilCommon.getAnnotationForDeleteAndSelectForPrimaryKeyColumns(introspectedTable)+"\")");
        return method;
    }

    protected Method selectByPrimaryKeyWithCacheForFetchLaterMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("getWithCacheForFetchLater");
        method.setReturnType(modelType);
        List<IntrospectedColumn> introspectedColumns;
        MybatisUtilCommon.addParameterForPrimaryKeyColumnsInMethodSelectByPrimaryKey(method, introspectedTable);
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("selectByPrimaryKey");
        sb.append("(");
        sb.append(MybatisUtilCommon.getParameterValueForPrimaryKeyColumns(introspectedTable));
        sb.append(");");
        method.addBodyLine(sb.toString());
        method.addAnnotation("@CacheForFetchLater");
        return method;
    }

    protected Method selectByPrimaryKeyWithoutCacheMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("getWithoutCache");
        method.setReturnType(modelType);
        List<IntrospectedColumn> introspectedColumns;
        MybatisUtilCommon.addParameterForPrimaryKeyColumnsInMethodSelectByPrimaryKey(method, introspectedTable);
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("selectByPrimaryKey");
        sb.append("(");
        sb.append(MybatisUtilCommon.getParameterValueForPrimaryKeyColumns(introspectedTable));
        sb.append(");");
        method.addBodyLine(sb.toString());
        return method;
    }


    protected Method selectByPrimaryKeyCacheMockThenFetchLater(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("getCacheMockThenFetchLater");
        method.setReturnType(modelType);
        List<IntrospectedColumn> introspectedColumns;
        MybatisUtilCommon.addParameterForPrimaryKeyColumnsInMethodSelectByPrimaryKey(method, introspectedTable);
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("selectByPrimaryKey");
        sb.append("(");
        sb.append(MybatisUtilCommon.getParameterValueForPrimaryKeyColumns(introspectedTable));
        sb.append(");");
        method.addBodyLine(sb.toString());
        method.addAnnotation("@CacheMockThenFetchLater");
        return method;
    }



    private Method updateMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("update");
        method.setReturnType(integer);
        method.addParameter(new Parameter(modelType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(".setAllFieldsNotNull();");
        method.addBodyLine(sb.toString());
        sb.setLength(0);
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("updateByPrimaryKeySelective");
        sb.append("(");
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(");");
        method.addBodyLine(sb.toString());
        method.addAnnotation("@CacheEvict(value = \"cache:" + modelType.getShortName().toLowerCase()
                + "\", key = \"'cache:" + modelType.getShortName().toLowerCase() + ":get'"
                + MybatisUtilCommon.getAnnotationForUpdateForPrimaryKeyColumns(introspectedTable, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName())) + "\")");
        return method;
    }

    private Method updateByExampleMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.addAnnotation("@CacheEvict(value = \"cache:" + modelType.getShortName().toLowerCase()
                + "\", key = \"'cache:" + modelType.getShortName().toLowerCase() + ":get'"
                + MybatisUtilCommon.getAnnotationForUpdateForPrimaryKeyColumns(introspectedTable, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName())) + "\")");
        method.setName("updateByExample");
        method.setReturnType(integer);
        method.addParameter(new Parameter(modelType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName())));
        method.addParameter(new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()) + "Example"));
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.setLength(0);
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(".setAllFieldsNotNull();");
        method.addBodyLine(sb.toString());
        sb.setLength(0);
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("updateByExample");
        sb.append("(");
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()));
        sb.append(",");
        sb.append(MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelType.getShortName()) + "Example");
        sb.append(");");
        method.addBodyLine(sb.toString());
        return method;
    }

    private Method deleteByPrimaryKeyMethod(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("delete");
        method.setReturnType(integer);
        MybatisUtilCommon.addParameterForPrimaryKeyColumnsInMethodSelectByPrimaryKey(method, introspectedTable);
        method.setVisibility(JavaVisibility.PUBLIC);
        StringBuilder sb = new StringBuilder();
        sb.append("return ");
        sb.append(getMapperShortWithDot());
        sb.append("deleteByPrimaryKey");
        sb.append("(");
        sb.append(MybatisUtilCommon.getParameterValueForPrimaryKeyColumns(introspectedTable));
        sb.append(");");
        method.addBodyLine(sb.toString());
        method.addAnnotation("@CacheEvict(value = \"cache:" + modelType.getShortName().toLowerCase()
                + "\", key = \"'cache:" + modelType.getShortName().toLowerCase() + ":get'"+MybatisUtilCommon.getAnnotationForDeleteAndSelectForPrimaryKeyColumns(introspectedTable)+"\")");
        return method;
    }

    private Method list(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("list");
        method.setReturnType(listT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine(
                    "return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExampleWithBLOBs("
                            + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        method.addAnnotation("@CacheForFetchLater");
        return method;
    }


    private Method listCacheMockThenFetchLater(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("listCacheMockThenFetchLater");
        method.setReturnType(listT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine(
                    "return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExampleWithBLOBs("
                            + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        method.addAnnotation("@CacheMockThenFetchLater");
        return method;
    }

    private Method listWithoutCache(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("listWithoutCache");
        method.setReturnType(listT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine(
                    "return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExampleWithBLOBs("
                            + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".selectByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        return method;
    }

    private Method countByExample(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("countByExample");
        method.setReturnType(longT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        method.addAnnotation("@CacheForFetchLater");
        return method;
    }

    private Method countByExampleWithoutCache(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("countByExampleWithoutCache");
        method.setReturnType(longT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        return method;
    }

    private Method countByExampleCacheMockThenFetchLater(IntrospectedTable introspectedTable, String tableName) {
        Method method = new Method();
        method.setName("countByExampleCacheMockThenFetchLater");
        method.setReturnType(longT);
        method.addParameter(
                new Parameter(modelExampleType, MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName())));
        method.setVisibility(JavaVisibility.PUBLIC);
        if (introspectedTable.hasBLOBColumns()) {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        } else {
            method.addBodyLine("return " + MybatisUtilCommon.toLowerCaseAtFirstCharacter(mapperType.getShortName()) + ".countByExample("
                    + MybatisUtilCommon.toLowerCaseAtFirstCharacter(modelExampleType.getShortName()) + ");");
        }
        method.addAnnotation("@CacheMockThenFetchLater");
        return method;
    }

}
